package net.qiqb.core.executing;

import cn.hutool.core.util.TypeUtil;
import net.qiqb.core.domain.DomainUtil;
import net.qiqb.core.domain.annotation.AggregatePersistence;
import net.qiqb.core.domain.annotation.AttachedAggregatePersistence;

import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;

public class AggregatePersistenceRegistrar {

    private final Map<String, AggregatePersistence<?>> aggregatePersistenceMap = new HashMap<>();
    // key ： 聚合根命令+ 附属名称
    private final Map<String, AttachedAggregatePersistence<?, ?>> attachedAggregatePersistenceMap = new HashMap<>();


    public boolean registerAttached(AttachedAggregatePersistence<?, ?> attachedAggregatePersistence) {
        final Map<Type, Type> typeMap = TypeUtil.getTypeMap(attachedAggregatePersistence.getClass());
        String[] key = new String[2];
        for (Map.Entry<Type, Type> entry : typeMap.entrySet()) {

            if (entry.getKey() instanceof TypeVariable<?> typeVariable) {
                final GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
                if (genericDeclaration.equals(AttachedAggregatePersistence.class)) {
                    final Type value = entry.getValue();
                    if (value instanceof Class<?> valueClass) {
                        if ("AR".equals(typeVariable.getName())) {
                            key[0] = valueClass.getName();
                        }
                        if ("D".equals(typeVariable.getName())) {
                            key[1] = valueClass.getName();
                        }

                    }
                }
            }
        }
        if (key[0] != null && key[1] != null) {
            this.attachedAggregatePersistenceMap.put(key[0] + ":" + key[1], attachedAggregatePersistence);
            return true;
        }
        return false;
    }


    public boolean register(AggregatePersistence<?> aggregatePersistence) {
        final Map<Type, Type> typeMap = TypeUtil.getTypeMap(aggregatePersistence.getClass());
        for (Map.Entry<Type, Type> entry : typeMap.entrySet()) {
            final Type key = entry.getKey();
            if (key instanceof TypeVariable<?> typeVariable) {
                final GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
                if (genericDeclaration.equals(AggregatePersistence.class)) {
                    final Type value = entry.getValue();
                    if (value instanceof Class<?> valueClass) {
                        this.aggregatePersistenceMap.put(valueClass.getName(), aggregatePersistence);
                        return true;
                    }
                }
            }
        }
        return false;
    }

    public <AR, D> AttachedAggregatePersistence<AR, D> getAttachedAggregatePersistence(Class<?> attachedAggregateClass) {
        final Class<?> aggregateRootClass = DomainUtil.getAggregateRootClass(attachedAggregateClass);
        if (aggregateRootClass == null) {
            return null;
        }
        final String aggregateName = DomainUtil.getAggregateName(aggregateRootClass);
        return (AttachedAggregatePersistence<AR, D>) attachedAggregatePersistenceMap.get(aggregateName + ":" + attachedAggregateClass.getName());
    }

    public <T> AggregatePersistence<T> getAggregatePersistence(String aggregateName) {
        return (AggregatePersistence<T>) aggregatePersistenceMap.get(aggregateName);
    }

    public <T> AggregatePersistence<T> getAggregatePersistence(Class<T> aggregateRootClass) {
        final String aggregateName = DomainUtil.getAggregateName(aggregateRootClass);
        if (aggregateName == null) {
            return null;
        }
        return (AggregatePersistence<T>) aggregatePersistenceMap.get(aggregateName);
    }
}
